package model

import (
	"beergo/lib/event"
	"beergo/lib/mysql"
	"beergo/lib/tool"
	"fmt"
	"github.com/astaxie/beego/logs"
	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/server/web/context"
	"reflect"
	"strconv"
	"strings"
)

type BaseModel struct {
	qb  *mysql.QueryBuilder
	ctx *context.Context
	isEventOff bool
}

func (this *BaseModel) GetIsEventOn() bool {
	return this.isEventOff
}
func (this *BaseModel) SetEventOn() *BaseModel {
	this.isEventOff = false
	return this
}
func (this *BaseModel) SetEventOff() *BaseModel {
	this.isEventOff = true
	return this
}
func (this *BaseModel) Trigger(eventName string,data interface{}) *BaseModel {
	if !this.isEventOff {
		event.DispatchToMany(eventName, data, this.GetCtx())
	}
	return this
}

func (this *BaseModel) SetCtx(ctx *context.Context) *BaseModel {
	this.ctx = ctx
	return this
}
func (this *BaseModel) GetCtx() *context.Context {
	return this.ctx
}

func (this *BaseModel) SetQueryBuilder(qb *mysql.QueryBuilder) *BaseModel {
	this.qb = qb
	return this
}
func (this *BaseModel) GetQueryBuilder() *mysql.QueryBuilder {
	return this.qb
}

func (this BaseModel) CheckOrmGroup(findIn string, find string) bool {
	return tool.HasSameWord(findIn, find)
}

func (this BaseModel) GetOrmGroups(model interface{}, field string) string {
	filed, find := reflect.TypeOf(model).FieldByName(field)
	if find {
		return filed.Tag.Get("groups")
	}
	return ""
}

func (this BaseModel) LogOut(info interface{}) {
	err := logs.GetLogger("INFO").Output(1, fmt.Sprintln(info))
	if err != nil {
		this.LogOut(err)
	}
}

func (this BaseModel) GetOrm() orm.Ormer {
	o := orm.NewOrm()
	return o
}

type ModelInterface interface {
	ToArrayGroup(groups string) map[string]interface{}
}

func (this BaseModel) GetSonsArrayCommon(groups string, args ...ModelInterface) []map[string]interface{} {
	var arraySons []map[string]interface{}
	for _, item := range args {
		this.GetOrm().Read(&item)
		arraySons = append(arraySons, item.ToArrayGroup(groups))
	}
	return arraySons
}

func (this BaseModel) ValueOf(model interface{}, fields string) reflect.Value {
	strSlice := strings.Split(fields, ".")
	var value reflect.Value
	value = reflect.ValueOf(model)
	for index, field := range strSlice {
		if index == 0 {
			value = value.FieldByName(tool.Ucfirst(field))
		} else {
			value = value.Elem().FieldByName(tool.Ucfirst(field))
		}
	}
	return value
}

func (this BaseModel) ValueOfString(model interface{}, fields string) string {
	value := this.ValueOf(model, fields)
	return value.String()
}

func (this BaseModel) ValueOfInt(model interface{}, fields string) int {
	value := this.ValueOfString(model, fields)
	intValue, _ := strconv.Atoi(value)
	return intValue
}

func (this BaseModel) ValueOfInt64(model interface{}, fields string) int64 {
	value := this.ValueOfString(model, fields)
	intValue, _ := strconv.ParseInt(value, 10, 64)
	return intValue
}

func (this *BaseModel) CallAndGet(model interface{}, methodName string, args ...interface{}) []reflect.Value {
	inputs := make([]reflect.Value, len(args))
	for i, _ := range args {
		inputs[i] = reflect.ValueOf(args[i])
	}
	//动态调用方法
	reflect.ValueOf(model).MethodByName(methodName).Call(inputs)
	return inputs
}

func (this *BaseModel) CallAndGetInt64(model interface{}, methodName string, args ...interface{}) (int64, bool) {
	outputs := this.CallAndGet(model, methodName, args)
	if len(outputs) == 0 {
		return 0, false
	}
	intValue, err := strconv.ParseInt(outputs[0].String(), 10, 64)
	if err != nil {
		return 0, false
	}
	return intValue, true
}

func (this *BaseModel) CallAndGetInt(model interface{}, methodName string, args ...interface{}) (int, bool) {
	outputs := this.CallAndGet(model, methodName, args)
	if len(outputs) == 0 {
		return 0, false
	}
	intValue, err := strconv.Atoi(outputs[0].String())
	if err != nil {
		return 0, false
	}
	return intValue, true
}

func (this *BaseModel) CallAndGetString(model interface{}, methodName string, args ...interface{}) (string, bool) {
	outputs := this.CallAndGet(model, methodName, args)
	if len(outputs) == 0 {
		return "", false
	}
	return outputs[0].String(), true
}
